package com.yunji.order.business;

import com.google.common.collect.Lists;
import com.yunji.common.exception.ServiceException;
import com.yunji.common.utils.ConvertDataUtil;
import com.yunji.common.utils.bean.BeanUtils;
import com.yunji.common.utils.uuid.UUIDUtils;
import com.yunji.order.bo.GetAuditApplyBO;
import com.yunji.order.command.retund.RefundStateMachine;
import com.yunji.order.context.convert.RefundConvert;
import com.yunji.order.domain.OrderDO;
import com.yunji.order.domain.OrderDetailDO;
import com.yunji.order.domain.RefundDO;
import com.yunji.order.domain.RefundDetailDO;
import com.yunji.order.dto.*;
import com.yunji.order.dto.cmd.*;
import com.yunji.order.emuns.RefundStatusEnum;
import com.yunji.order.emuns.RefundTypeEnum;
import com.yunji.order.service.OrderService;
import com.yunji.order.service.RefundService;
import com.yunji.order.vo.RefundDetailVO;
import com.yunji.order.vo.RefundVO;
import com.yunji.order.wechat.WechatCallbackDTO;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * @author : peter-zhu
 * @date : 2025/3/29 15:25
 * @description : 我要让退款业务变得简单
 */
@Slf4j
@Component
public class RefundBusiness {
    @Resource
    OrderService orderService;
    @Resource
    private RefundService refundService;
    @Resource
    private WechatBusiness wechatBusiness;

    public Object createPlateRefund(PlateRefundCreateDTO requestData) {
        RefundDO rs = (RefundDO) createRefund(requestData);
        if (rs != null)
            return RefundStateMachine.statusChange(new RefundPassCommandDTO(refundService.getById(rs.getRefundId()),
                    RefundStatusEnum.PASS, requestData.getAuditBackStock()));
        return false;
    }

    // 禁止在创建退款堆其他业务的代码(比如库存 分润)  创建退款只有退款相关业务 如果有其他业务 请发布事件
    public Object createRefund(RefundCreateDTO requestData) {
        Long orderId = requestData.getOrderId();
        OrderDO order = orderService.getOrderById(orderId);
        ServiceException.throwException(order == null, "退款订单号不存在" + orderId);
        List<RefundDetailDTO> refundDetails = requestData.getOrderDetails();
        if (refundDetails.isEmpty())
            throw new ServiceException("认真点,退款明细怎能为空?");
        Map<Long, OrderDetailRefundDTO> refundNumsMap = refundService.getRefundDetailNumsMap(Lists.newArrayList(orderId));
        List<OrderDetailDO> orderDetails = orderService.getOrderDetailsByOid(orderId);
        Map<Long, OrderDetailDO> orderDetailMap =
                ConvertDataUtil.list2Map(orderDetails, OrderDetailDO::getOrderDetailId);
        //订单明细数量和接口传来的明细数量 相等才可能是全部退款 当然还要比较每个明细里的数量是否相等
        boolean wholeRefund = orderDetails.size() == requestData.getOrderDetails().size();
        BigDecimal refundDetailSumAmt = BigDecimal.ZERO;
        BigDecimal orderDetailAmt = orderDetails.stream().map(OrderDetailDO::getPayableAmount).reduce(BigDecimal::add).orElse(BigDecimal.ZERO);
        List<RefundDetailDO> detailDOS = Lists.newArrayList();
        for (RefundDetailDTO rd : refundDetails) {
            OrderDetailDO orderDetailDO = orderDetailMap.get(rd.getOrderDetailId());
            if (orderDetailDO == null)
                throw new ServiceException("数据异常,请求退款单明细id" + rd.getOrderDetailId() + "不存在");
            int availableRefundNums = orderDetailDO.getProductNums();
            OrderDetailRefundDTO odr = refundNumsMap.get(rd.getOrderDetailId());
            if (odr != null)
                availableRefundNums = availableRefundNums - odr.getRefundNums();
            if (availableRefundNums - rd.getProductNums() < 0)// 每个明细的可退款数量减已退款数量<0 说明请求退款数量超出
                throw new ServiceException("退款数量超出,请求退款单明细id是" + rd.getOrderDetailId()
                        + "可退款数量最大为" + availableRefundNums);
            if (wholeRefund && !rd.getProductNums().equals(orderDetailDO.getProductNums()))
                wholeRefund = false;//订单明细数量只要有一次与请求明细数量不相等,说明就不是全部退款 如果是全部退款 退款状态机会触发退优惠券的订阅事件
            BigDecimal refundDetailAmt = orderDetailDO.getPayableUnitAmount().multiply(new BigDecimal(rd.getProductNums()));
            refundDetailSumAmt = refundDetailSumAmt.add(refundDetailAmt);//计算出退款明细应付金额总额 为算出退款比例
            RefundDetailDO rdDomain = new RefundDetailDO();
            rdDomain.setRefundNums(rd.getProductNums());
            rdDomain.setOrderId(orderId);
            rdDomain.setOrderCode(order.getOrderCode());
            rdDomain.setOrderDetailId(orderDetailDO.getOrderDetailId());
            rdDomain.setProductId(orderDetailDO.getProductId());
            rdDomain.setRefundAmount(refundDetailAmt);
            detailDOS.add(rdDomain);
        }
        //要退款的商品销售价小计 / 订单销售价合计金额 算出退款比例保留四位
        BigDecimal refundRatio = refundDetailSumAmt.divide(orderDetailAmt, 4, RoundingMode.DOWN);
        String refundCode = UUIDUtils.refundCode();
        RefundDO refund = new RefundDO();
        refund.setRefundCode(refundCode);
        //计算退款金额：如果全部退款直接拿订单实付总金额 部分退款用订单实付总金额乘退款比例保留两位
        refund.setWholeRefund(wholeRefund ? 1 : 0);
//        refund.setRefundAmount(wholeRefund ? order.getActualAmount() :
//                order.getActualAmount().multiply(refundRatio).setScale(2, RoundingMode.DOWN));
        refund.setRefundAmount(wholeRefund ? order.getActualAmount() : refundDetailSumAmt.setScale(2, RoundingMode.DOWN));
        refund.setRefundRatio(wholeRefund ? BigDecimal.ONE : refundRatio);
        refund.setTakenOut(requestData.getTakenOut());
        refund.setOrderId(orderId);
        refund.setOrderCode(order.getOrderCode());
        refund.setBelongId(order.getBelongId());
        if (requestData instanceof PlateRefundCreateDTO) {
            PlateRefundCreateDTO plate = (PlateRefundCreateDTO) requestData;
            refund.setAuditBackStock(plate.getAuditBackStock());
            refund.setRefundType(RefundTypeEnum.PLATE.getCode());
        } else if (requestData instanceof AppRefundCreateDTO) {
            refund.setRefundType(RefundTypeEnum.USER.getCode());
            AppRefundCreateDTO app = (AppRefundCreateDTO) requestData;
            refund.setIssue(app.getIssue());
            refund.setIssuePics(app.getIssuePics());
        }
        refund.setApplyStatus(RefundStatusEnum.APPLYING.getCode());
        refund.setApplyReasonCode(requestData.getApplyReasonCode());
        refund.setApplyReason(requestData.getApplyReason());
        refund.setApplyTime(new Date());
        refund.setRefundNums(detailDOS.stream().map(RefundDetailDO::getRefundNums).reduce(Integer::sum).orElse(0));
        return RefundStateMachine.statusChange(new RefundCreateCommandDTO(refund, RefundStatusEnum.APPLYING, wholeRefund, detailDOS));//触发退款状态机
    }

    public void cancelRefund(Long refundId) {
        RefundStateMachine.statusChange(new RefundCommandDTO(refundService.getById(refundId), RefundStatusEnum.CANCELED));
    }

    public void passRefund(Long refundId, Integer auditBackStock) {
        RefundStateMachine.statusChange(new RefundPassCommandDTO(refundService.getById(refundId), RefundStatusEnum.PASS, auditBackStock));
    }

    public void rejectRefund(Long refundId, String rejectReason) {
        RefundStateMachine.statusChange(new RefundRejectCommandDTO(refundService.getById(refundId), RefundStatusEnum.REJECTED, rejectReason));
    }

    public Map<String, Object> refundCallback(HttpServletRequest request) {
        WechatCallbackDTO rs = wechatBusiness.refundCallback(request);
        if (rs.isSuccess()) {
            RefundDO refundDO = refundService.getByCode(rs.getTradeCode());
            RefundStateMachine.statusChange(
                    new RefundFinishCommandDTO(refundDO, RefundStatusEnum.REFUNDED, rs));
        }
        return WechatBusiness.buildSuccess();
    }

    public List<RefundDO> searchAuditRefund(GetAuditApplyBO bo) {
        return refundService.searchAuditRefund(bo);
    }

    public RefundVO getByRefundId(Long refundId) {
        RefundVO refundVO = new RefundVO();
        RefundDO domain = refundService.getById(refundId);
        OrderDO order = orderService.getOrderById(domain.getOrderId());
        BeanUtils.clone(order, refundVO);
        BeanUtils.clone(domain, refundVO);
        RefundConvert.convertVO(refundVO);
        refundVO.setDetailList(refundService.refundDetailList(RefundDetailParam.builder().refundId(refundVO.getRefundId()).build()));
        return refundVO;
    }

    public List<RefundVO> refundList(RefundParam param) {
        List<RefundVO> refundVOS = refundService.refundList(param);
        RefundConvert.convertVO(refundVOS);
        return refundVOS;
    }

    public List<RefundVO> appListByOid(Long orderId) {
        RefundParam refundParam = new RefundParam();
        refundParam.setOrderId(orderId);
        List<RefundVO> refundVOS = refundService.refundList(refundParam);
        Map<Long, List<RefundDetailVO>> detailMapByRids = refundService.getDetailVOMapByRids(refundVOS.stream()
                .map(RefundVO::getRefundId).collect(Collectors.toList()));
        for (RefundVO vo : refundVOS) {
            vo.setDetailList(detailMapByRids.get(vo.getRefundId()));
        }
        return refundVOS;
    }
}
